package co.edu.uniandes.distribuidos.checkpoint;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;

import co.edu.uniandes.distribuidos.checkpoint.events.Event;
import co.edu.uniandes.distribuidos.checkpoint.events.EventContainer;
import co.edu.uniandes.distribuidos.checkpoint.exception.CheckpointManagerException;
import co.edu.uniandes.distribuidos.peer.Peer;

public class CheckpointManager {

	private FileWriter writer;
	
	private EventContainer container;
	
	private int consecutive=1;
	
	private static final String LOGFILE="log.xml";
	
	private static CheckpointManager singleton;
	
	
	
	public static CheckpointManager getInstance() throws CheckpointManagerException{
		if(singleton==null){
			singleton=new CheckpointManager();
		}
		return singleton;
	}
	
	
	
	private CheckpointManager() throws CheckpointManagerException{
		init();
	}
	
	private void init() throws CheckpointManagerException {
		
		//create file writer and a file to store the events
		
		try {
			File logFile = new File(LOGFILE);
	        if(!logFile.exists() || !logFile.isFile()){
	        	boolean success = logFile.createNewFile();
	        	if(success){
	        		this.container=new EventContainer();
	        		writeLog();
	        	}else{
	        		throw new CheckpointManagerException("Imposible crear el archivo de log");
	        	}
	        }else{
				load();
	        }
		} catch (Exception e) {
			e.printStackTrace();
			throw new CheckpointManagerException(e);
		}
	}
	
	private void writeLog() throws CheckpointManagerException{
		
		try {
			File file = new File(LOGFILE);
			writer = new FileWriter(file);
			Marshaller.marshal(container, writer);
		} catch (Exception e) {
			e.printStackTrace();
			throw new CheckpointManagerException(e);
		}
		
	}

	public void save(Event ev) throws CheckpointManagerException{

		// Marshall the events to log
		try {
			ev.setConsecutive(generateEventID());
			container.add(ev);
			writeLog();
		} catch (Exception e) {
			e.printStackTrace();
			throw new CheckpointManagerException(e);
		} 
	}
	
	public void load() throws CheckpointManagerException{

		// Unmarshall the log
		try {
			File file=new File(LOGFILE); 
			
		    Unmarshaller un = new Unmarshaller(EventContainer.class);

		    // read in the StorageInfoContainer using the mapping
		    FileReader in = new FileReader(file);
		    container = (EventContainer) un.unmarshal(in);
		    in.close();
			if(container==null){
				container=new EventContainer();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new CheckpointManagerException(e);
		} 
		
	}
	
	public boolean existEvent(){
		if(container!=null&&!container.isEmpty()){
			return true;
		}else{
			return false;
		}
		
	}
	
	/**
	 * Generates an unique event id
	 * @return
	 */
	private int generateEventID(){
		return consecutive++;
	}
	
	public void remove(Event ev) throws CheckpointManagerException{
		try{
			if(ev!=null){
				container.remove(ev);
				writeLog();
			}
		}catch (Exception e) {
			e.printStackTrace();
			throw new CheckpointManagerException(e);
		}
	}
	
	public void removeEvents() throws CheckpointManagerException{
		try{
			container.removeAll();
			writeLog();
		}catch (Exception e) {
			e.printStackTrace();
			throw new CheckpointManagerException(e);
		}
	}
	
	public void restore(Peer peer) throws CheckpointManagerException{
		if(container!=null&&!container.isEmpty()){
			List<Event> events=new ArrayList<Event>();
			events.addAll(container.getEvents());
			removeEvents();
			for(Event ev:events){
				ev.execute(peer);
			}
		}
	}

}
